package name.cyrax.red.jpv.impl.vectors;

import name.cyrax.red.jpv.api.adapters.NumericAccessor;
import name.cyrax.red.jpv.api.vectors.Vector;
import name.cyrax.red.jpv.api.vectors.VectorFactory;
import name.cyrax.red.jpv.api.vectors.VectorWriter;

public final class VectorFactories
{
	private VectorFactories() {};
	
	public static VectorFactory<Integer> getInteger()
	{
		return IntegerFactorySingleton.INSTANCE;
	}

	public static VectorFactory<Long> getLong()
	{
		return LongFactorySingleton.INSTANCE;
	}

	public static VectorFactory<Double> getDouble()
	{
		return DoubleFactorySingleton.INSTANCE;
	}

	public static <N extends Number> VectorFactory<Number> getNumber()
	{
		return NumberFactorySingleton.INSTANCE;
	}

	private static class IntegerFactorySingleton
	{
		public static final VectorFactory<Integer> INSTANCE = new VectorFactory<Integer>() {

			@Override public VectorWriter<Integer> newInstance(final int size)
			{
				return new VectorWriter<Integer>() {
					private int[] result = new int[size];

					@Override public void set(int index, int v)
					{
						result[index] = v;
					}

					@Override public void set(int index, long v)
					{
						result[index] = (int) v;
					}

					@Override public void set(int index, double v)
					{
						result[index] = (int) v;
					}

					@Override public void set(int index, Number v)
					{
						result[index] = v.intValue();
					}

					@Override public Vector<Integer> done()
					{
						int[] r = result;
						result = null;
						return Vectors.newInstance0(r);
					}
					
				};
			}

			@Override public VectorWriter<Integer> newBoxedInstance(int size, Class<? extends Integer> boxedType)
			{
				return newBoxedInstance0(size, boxedType);
			}

			@Override public <T> T detectType(NumericAccessor<T> accessor)
			{
				return accessor.asInteger(null);
			}
		};
	}

	private static <T extends Number> VectorWriter<T> newBoxedInstance0(int size, Class<? extends T> boxedType)
	{
		@SuppressWarnings("unchecked") VectorWriter<T> r = 
				(VectorWriter<T>) NumberFactorySingleton.INSTANCE.newBoxedInstance(size, boxedType);
		return r;
	}
	
	private static class LongFactorySingleton
	{
		public static final VectorFactory<Long> INSTANCE = new VectorFactory<Long>() {

			@Override public VectorWriter<Long> newInstance(final int size)
			{
				return new VectorWriter<Long>() {
					private long[] result = new long[size];

					@Override public void set(int index, int v)
					{
						result[index] = v;
					}

					@Override public void set(int index, long v)
					{
						result[index] = v;
					}

					@Override public void set(int index, double v)
					{
						result[index] = (long) v;
					}

					@Override public void set(int index, Number v)
					{
						result[index] = v.longValue();
					}

					@Override public Vector<Long> done()
					{
						long[] r = result;
						result = null;
						return Vectors.newInstance0(r);
					}
					
				};
			}

			@Override public VectorWriter<Long> newBoxedInstance(int size, Class<? extends Long> boxedType)
			{
				return newBoxedInstance0(size, boxedType);
			}

			@Override public <T> T detectType(NumericAccessor<T> accessor)
			{
				return accessor.asLong(null);
			}			
		};
	}
	
	private static class DoubleFactorySingleton
	{
		public static final VectorFactory<Double> INSTANCE = new VectorFactory<Double>() {

			@Override public VectorWriter<Double> newInstance(final int size)
			{
				return new VectorWriter<Double>() {
					private double[] result = new double[size];

					@Override public void set(int index, int v)
					{
						result[index] = v;
					}

					@Override public void set(int index, long v)
					{
						result[index] = v;
					}

					@Override public void set(int index, double v)
					{
						result[index] = v;
					}

					@Override public void set(int index, Number v)
					{
						result[index] = v.doubleValue();
					}

					@Override public Vector<Double> done()
					{
						double[] r = result;
						result = null;
						return Vectors.newInstance0(r);
					}
					
				};
			}

			@Override public VectorWriter<Double> newBoxedInstance(int size, Class<? extends Double> boxedType)
			{
				return newBoxedInstance0(size, boxedType);
			}

			@Override public <T> T detectType(NumericAccessor<T> accessor)
			{
				return accessor.asDouble(null);
			}			
		};
	}

	private static class NumberFactorySingleton
	{
		public static final VectorFactory<Number> INSTANCE = new VectorFactory<Number>() {

			@Override public VectorWriter<Number> newInstance(int size)
			{
				throw new UnsupportedOperationException();
				
//				final Number[] result = new Number[size];
//				return new VectorWriter<Number>() {
//
//					@Override public void set(int index, int v)
//					{
//						result[index] = v;
//					}
//
//					@Override public void set(int index, long v)
//					{
//						result[index] = v;
//					}
//
//					@Override public void set(int index, double v)
//					{
//						result[index] = v;
//					}
//
//					@Override public void set(int index, Number v)
//					{
//						result[index] = v.doubleValue();
//					}
//
//					@Override public Vector<Number> done()
//					{
//						// TODO Auto-generated method stub
//						return null;
//					}
//					
//				};
			}

			@Override public VectorWriter<Number> newBoxedInstance(int size, Class<? extends Number> boxedType)
			{
				throw new UnsupportedOperationException();
			}

			@Override public <T> T detectType(NumericAccessor<T> accessor)
			{
				throw new UnsupportedOperationException();
			}			
		};
	}
}
